package protocol

import (
	"JoRedis/lib/logger"
	"bufio"
	"errors"
	"io"
	"runtime/debug"
	"strconv"
)

type cmdState struct {
	// 解析的数据是单行还是多行
	multiLineFlag bool
	// 指令的参数个数
	cmdArgNum int
	// 指令类型(*,$,+)
	cmdType byte
	// 指令的参数
	cmdArgBytesSlc [][]byte
	// 数据的长度
	bulkLen int64
}

// 判断一条命令是否解析完成
func (this *cmdState) finish() bool {
	if this.cmdArgNum > 0 && this.cmdArgNum == len(this.cmdArgBytesSlc) {
		return true
	} else {
		return false
	}
}

// ParseProtocol 协议解析
func ParseProtocol(reader io.Reader) <-chan *Payload {
	payloadPtrChn := make(chan *Payload)
	go parse(reader, payloadPtrChn)
	return payloadPtrChn
}

// 客户端发送的命令读取和解析
// *3\r\n$3\r\nset\r\n$6\r\nauthor\r\n$8\r\ncodehole\r\n
func parse(reader io.Reader, payloadPtrChn chan<- *Payload) {
	defer func() {
		if err := recover(); err != nil {
			logger.Error(err, string(debug.Stack()))
		}
	}()
	var cmdStateStu cmdState
	readerPtr := bufio.NewReader(reader)
	for {
		var cmdBytes []byte
		var err error
		if cmdStateStu.bulkLen == 0 {
			// 解析$
			cmdBytes, err = readerPtr.ReadBytes('\n')
			// socket数据读取异常
			if err != nil {
				payloadPtrChn <- &Payload{
					Err: err,
				}
				close(payloadPtrChn)
				return
			}
			if len(cmdBytes) == 0 || cmdBytes[len(cmdBytes)-2] != '\r' {
				payloadPtrChn <- &Payload{
					Err: errors.New("protocol error: " + string(cmdBytes)),
				}
				cmdStateStu = cmdState{}
				continue
			}
		} else {
			// 解析$后的参数
			bulkLen := cmdStateStu.bulkLen + 2
			cmdBytes = make([]byte, bulkLen)
			// 读取bulkLen长度的字节
			_, err = io.ReadFull(readerPtr, cmdBytes)
			// socket数据读取异常
			if err != nil {
				payloadPtrChn <- &Payload{
					Err: err,
				}
				close(payloadPtrChn)
				return
			}
			if len(cmdBytes) == 0 || cmdBytes[len(cmdBytes)-2] != '\r' || cmdBytes[len(cmdBytes)-1] != '\n' {
				// 无效的$后参数
				payloadPtrChn <- &Payload{
					Err: errors.New("protocol error: " + string(cmdBytes)),
				}
				cmdStateStu = cmdState{}
				continue
			}
		}

		if !cmdStateStu.multiLineFlag {
			// 单行解析或者新的指令
			if cmdBytes[0] == '*' {
				cmdNumBytes := cmdBytes[1 : len(cmdBytes)-2]
				cmdNum, err := strconv.ParseInt(string(cmdNumBytes), 10, 32)
				if err != nil {
					payloadPtrChn <- &Payload{
						Err: errors.New("protocol error: " + err.Error()),
					}
					cmdStateStu = cmdState{}
					continue
				}

				// 还未解析过
				if cmdNum == 0 {
					payloadPtrChn <- &Payload{
						DataReply: MakeEmptyMultiBulkReply(),
					}
					cmdStateStu = cmdState{}
					continue
				}
				cmdStateStu.cmdArgNum = int(cmdNum)
				// 标记为多行模式
				cmdStateStu.multiLineFlag = true
				cmdStateStu.cmdArgBytesSlc = make([][]byte, 0, cmdStateStu.cmdArgNum)
				cmdStateStu.cmdType = cmdBytes[0]
			} else if cmdBytes[0] == '$' {
				cmdNumBytes := cmdBytes[1 : len(cmdBytes)-2]
				cmdNum, err := strconv.ParseInt(string(cmdNumBytes), 10, 32)
				if err != nil {
					payloadPtrChn <- &Payload{
						Err: errors.New("protocol error: " + err.Error()),
					}
					cmdStateStu = cmdState{}
					continue
				}

				if cmdNum == -1 {
					payloadPtrChn <- &Payload{
						DataReply: MakeNullBulkReply(),
					}
				}
				cmdStateStu.multiLineFlag = true
				cmdStateStu.cmdArgNum = 1
				cmdStateStu.cmdArgBytesSlc = make([][]byte, 0, 1)
				cmdStateStu.cmdType = cmdBytes[0]
			} else {
				argLen := len(cmdBytes) - 2
				if cmdBytes[0] == '+' {
					// 简单字符串回复 +OK\r\n
					payloadPtrChn <- &Payload{
						DataReply: MakeStatusReply(string(cmdBytes[1:argLen])),
					}
				} else if cmdBytes[0] == '-' {
					// 错误回复 -ERR XXX\r\n
					payloadPtrChn <- &Payload{
						DataReply: MakeErrReply(string(cmdBytes[1:argLen])),
					}
				} else if cmdBytes[0] == ':' {
					// 整数回复 :22\r\n
					argInt, err := strconv.ParseInt(string(cmdBytes[1:argLen]), 10, 32)
					if err != nil {
						payloadPtrChn <- &Payload{
							Err: errors.New("protocol error: " + err.Error()),
						}
					}
					payloadPtrChn <- &Payload{
						DataReply: MakeIntReply(argInt),
					}
				}
				// 重置
				cmdStateStu = cmdState{}
			}
		} else {
			// 多行解析($,*)
			if cmdBytes[0] == '$' {
				bulkLenBytes := cmdBytes[1 : len(cmdBytes)-2]
				bulkLen, err := strconv.ParseInt(string(bulkLenBytes), 10, 32)
				if err != nil {
					// 数字转换失败
					payloadPtrChn <- &Payload{
						Err: errors.New("protocol error: " + err.Error()),
					}
				}
				cmdStateStu.bulkLen = bulkLen
			} else {
				// 解析出来的参数($后的参数)
				cmdArg := cmdBytes[:len(cmdBytes)-2]
				cmdStateStu.cmdArgBytesSlc = append(cmdStateStu.cmdArgBytesSlc, cmdArg)
				cmdStateStu.bulkLen = 0
			}
			// 判断命令是否解析完
			if cmdStateStu.finish() {
				if cmdStateStu.cmdType == '*' {
					payloadPtrChn <- &Payload{
						DataReply: MakeMultiBulkReply(cmdStateStu.cmdArgBytesSlc),
					}
				} else if cmdStateStu.cmdType == '$' {
					payloadPtrChn <- &Payload{
						DataReply: MakeBulkReply(cmdStateStu.cmdArgBytesSlc[0]),
					}
				}
				cmdStateStu = cmdState{}
			}
		}
	}
}
